package com.boarsoft.boar.batch.action;

import java.util.Date;

import org.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.Namespace;
import org.apache.struts2.convention.annotation.ParentPackage;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import com.boarsoft.boar.batch.biz.BatchInfoBiz;
import com.boarsoft.boar.batch.entity.BatchInfo;
import com.boarsoft.boar.batch.service.BatchDispatcher;
import com.boarsoft.boar.batch.service.BatchScheduler;
import com.boarsoft.boar.common.Constants;
import com.boarsoft.common.Authorized;
import com.boarsoft.common.Util;
import com.boarsoft.common.action.PageAction;
import com.boarsoft.common.dao.PagedResult;
import com.boarsoft.common.util.JsonUtil;

@SuppressWarnings("serial")
@Component
@Scope("prototype")
@ParentPackage("default-package")
@Namespace(value = "/info")
public class BatchInfoAction extends PageAction implements ApplicationContextAware {

	@Autowired
	private BatchInfoBiz infoBiz;

	@Autowired
	private BatchScheduler batchScheduler;

	private ApplicationContext applicationContext;

	private String id;

	private String key;

	private String catalog;

	private String code;

	private String name;

	private Short failable;

	private String follower;

	private Integer freeze;

	private String params;

	private Integer timeout;

	private String memo;

	private String startTime;

	private String stopTime;

	private String job;

	protected String impl;

	private String cron;

	protected int period;

	@Action("get")
	@Authorized(code = "batch.info.get")
	public void get() {
		if (Util.strIsEmpty(id)) {
			writeReply(Constants.ERROR_INVALID, "get");
			return;
		}
		BatchInfo o = infoBiz.get(id);
		writeReply(true, JsonUtil.from(o));
	}

	@Action("start")
	@Authorized(code = "batch.info.start")
	public void start() {
		if (Util.strIsEmpty(id)) {
			writeReply(Constants.ERROR_INVALID, "start");
			return;
		}
		BatchInfo o = infoBiz.get(id);
		impl = o.getImpl();
		if (Util.strIsEmpty(impl)) {
			writeReply(Constants.ERROR_INVALID, "impl");
			return;
		}
		BatchDispatcher bd = this.getDispatcher(impl);
		if (bd == null) {
			writeReply(Constants.ERROR_INVALID, "impl");
			return;
		}
		int r = bd.start(id, params);
		writeReply(true, String.valueOf(r));
	}

	public BatchDispatcher getDispatcher(String name) {
		if (!applicationContext.containsBean(name)) {
			return null;
		}
		return (BatchDispatcher) applicationContext.getBean(name);
	}

	@Action("enable")
	@Authorized(code = "batch.info.status")
	public void enable() {
		if (Util.strIsEmpty(id)) {
			writeReply(Constants.ERROR_INVALID, "enable");
			return;
		}
		if (!infoBiz.enable(id)) {
			writeReply(Constants.ERROR_UNKNOWN);
			return;
		}
		BatchInfo o = infoBiz.get(id);
		// 未配置CRON表达式的的批量不注册定时计划
		if (Util.strIsEmpty(o.getCron())) {
			writeReply();
			return;
		}
		Date d = Util.str2date(o.getStartTime(), Util.STDDTF);
		try {
			d = batchScheduler.schedule(o, d);
			writeReply(true, Util.date2str(d, Util.STDDTMF));
		} catch (ClassNotFoundException e) {
			log.error("Invalid job class {} of batch {}", o.getJob(), o, e);
			writeReply(Constants.ERROR_INVALID, "job");
		} catch (Exception e) {
			log.error("Error on schedule {} with cron {}", o, o.getCron(), e);
			infoBiz.disable(id);
			writeReply(Constants.ERROR_UNKNOWN);
		}
	}

	@Action("disable")
	@Authorized(code = "batch.info.status")
	public void disable() {
		if (Util.strIsEmpty(id)) {
			writeReply(Constants.ERROR_INVALID, "id");
			return;
		}
		if (!infoBiz.disable(id)) {
			writeReply(Constants.ERROR_UNKNOWN);
			return;
		}
		BatchInfo o = infoBiz.get(id);
		try {
			batchScheduler.unschedule(o.getId(), o.getImpl());
			writeReply();
		} catch (Exception e) {
			log.error("Error on disable batch {}", id, e);
			writeReply(Constants.ERROR_UNKNOWN);
		}
	}

	@Action("save")
	@Authorized(code = "batch.info.save")
	public void save() {
		if (Util.strIsEmpty(catalog) || Util.strIsEmpty(code) || Util.strIsEmpty(impl)) {
			writeReply(Constants.ERROR_INVALID, "save");
			return;
		}
		BatchInfo o = null;
		if (Util.strIsEmpty(id)) {
			o = new BatchInfo();
		} else {
			o = infoBiz.get(id);
			if (o.getStatus() == BatchInfo.STATUS_ENABLE) {
				writeReply(Constants.ERROR_INVALID, "save");
				return;
			}
		}
		o.setCatalog(catalog);
		o.setCode(code);
		o.setName(name);
		o.setFailable(failable);
		o.setFreeze(freeze);
		o.setTimeout(timeout);
		o.setParams(params);
		o.setImpl(impl);
		o.setCron(cron);
		o.setStartTime(startTime);
		o.setStopTime(stopTime);
		// o.setStatus(status);
		o.setJob(job);
		o.setPeriod(period);
		o.setMemo(memo);
		infoBiz.save(o);
		writeReply();
	}

	@Action("delete")
	@Authorized(code = "batch.info.delete")
	public void delete() {
		if (Util.strIsEmpty(id)) {
			writeReply(Constants.ERROR_INVALID, "delete");
			return;
		}
		infoBiz.delete(id);
		writeReply();
	}

	@Action("list")
	@Authorized(code = "batch.info.list")
	public void list() {
		PagedResult<BatchInfo> pr = infoBiz.list(catalog, key, orderBy, pageNo, pageSize);
		writeReply(true, JsonUtil.from(pr));
	}

	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public String getKey() {
		return key;
	}

	public void setKey(String key) {
		this.key = key;
	}

	public String getCatalog() {
		return catalog;
	}

	public void setCatalog(String catalog) {
		this.catalog = catalog;
	}

	public Short getFailable() {
		return failable;
	}

	public void setFailable(Short failable) {
		this.failable = failable;
	}

	public String getFollower() {
		return follower;
	}

	public void setFollower(String follower) {
		this.follower = follower;
	}

	public Integer getFreeze() {
		return freeze;
	}

	public void setFreeze(Integer freeze) {
		this.freeze = freeze;
	}

	public String getMemo() {
		return memo;
	}

	public void setMemo(String memo) {
		this.memo = memo;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getParams() {
		return params;
	}

	public void setParams(String params) {
		this.params = params;
	}

	public Integer getTimeout() {
		return timeout;
	}

	public void setTimeout(Integer timeout) {
		this.timeout = timeout;
	}

	public String getCode() {
		return code;
	}

	public void setCode(String code) {
		this.code = code;
	}

	public BatchInfoBiz getInfoBiz() {
		return infoBiz;
	}

	public void setInfoBiz(BatchInfoBiz infoBiz) {
		this.infoBiz = infoBiz;
	}

	public String getStartTime() {
		return startTime;
	}

	public void setStartTime(String startTime) {
		this.startTime = startTime;
	}

	public String getStopTime() {
		return stopTime;
	}

	public void setStopTime(String stopTime) {
		this.stopTime = stopTime;
	}

	public String getJob() {
		return job;
	}

	public void setJob(String job) {
		this.job = job;
	}

	public String getImpl() {
		return impl;
	}

	public void setImpl(String impl) {
		this.impl = impl;
	}

	public String getCron() {
		return cron;
	}

	public void setCron(String cron) {
		this.cron = cron;
	}

	public int getPeriod() {
		return period;
	}

	public void setPeriod(int period) {
		this.period = period;
	}

	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		this.applicationContext = applicationContext;
	}

	public ApplicationContext getApplicationContext() {
		return applicationContext;
	}

	public BatchScheduler getBatchScheduler() {
		return batchScheduler;
	}

	public void setBatchScheduler(BatchScheduler batchScheduler) {
		this.batchScheduler = batchScheduler;
	}
}
